//
// Created by wang on 2021/7/20.
//

#include "mp.h"
#include "cmn.h"
#include "enet.h"

#define ROUND_UP_4(s) (((s) + 3) & ~3)

pool_info g_pool_info;
static int32_t get_free_mp_id();

void mp_init() {
    memset(&g_pool_info, 0, sizeof(pool_info));
    g_pool_info.n = MP_MAX_POOL;

    g_pool_info.pools = (pool*) calloc(g_pool_info.n, sizeof(pool));
    assert(g_pool_info.pools);

    if (ENET_RET_SUCCESS != e_sem_init(&g_pool_info.sem, 0))
        abort();

    e_sem_post(&g_pool_info.sem);
}

ret_t mp_create(uint32_t sz, uint32_t cnt, uint8_t flags, mp_id *id) {
    mp_id mid = -1;
    uint8_t *base = NULL;
    mp_blk *f_p = NULL;
    pool *p;

    if (0 == sz || 0 == cnt || NULL == id)
        return ERR_UTIL_MP_INVALID_PARAMETERS;

    sz = ROUND_UP_4(sz);

    if ((mid = get_free_mp_id()) < 0) {
        return ERR_UTIL_MP_NO_FREE_POOL;
    }

    if (!(base = calloc(cnt + 1, sz))) {
        return ERR_UTIL_MP_NO_MORE_MEM;
    }

    p = &g_pool_info.pools[mid];
    p->base = base;
    p->start = (uint8_t*)ROUND_UP_4((uint64_t)base);
    p->end = base + (cnt + 1) * sz;
    p->blk_sz = sz;
    p->blk_cnt = cnt;
    p->free_cnt = cnt;
    p->free_blk = (mp_blk*)p->start;

    f_p = p->free_blk;
    while (--cnt) {
        f_p->next = (mp_blk*)((uint8_t *)f_p + p->blk_sz);
        f_p = f_p->next;
    }
    f_p->next = NULL;

    *id = mid;


    return ENET_RET_SUCCESS;
}

ret_t mp_delete(mp_id id) {
    if (0 == id || id >= MP_MAX_POOL) {
        return ERR_UTIL_MP_INVALID_PARAMETERS;
    }

    e_sem_wait(&g_pool_info.sem);
    if (0 != g_pool_info.pools[id].blk_cnt) {
        free(g_pool_info.pools[id].base);
        memset(&g_pool_info.pools[id], 0, sizeof(pool));
    }
    e_sem_post(&g_pool_info.sem);

    return ENET_RET_SUCCESS;
}

static int32_t get_free_mp_id() {
    int32_t idx;
    int32_t id = -1;
    e_sem_wait(&g_pool_info.sem);
    for (idx = 0; idx < g_pool_info.n; idx++) {
        if(0 == g_pool_info.pools[idx].blk_cnt) {
            g_pool_info.pools[idx].blk_cnt = 1;
            id = idx;
            break;
        }
    }
    e_sem_post(&g_pool_info.sem);
    return id;
}

ret_t mp_alloc(mp_id id, uint8_t **block) {
    pool *p = NULL;

    if (id < 0 || id >= MP_MAX_POOL || !block) {
        return ERR_UTIL_MP_INVALID_PARAMETERS;
    }

    p = (pool*)&g_pool_info.pools[id];

    if (p->free_blk) {
        *block = (uint8_t*)p->free_blk;
        p->free_blk = p->free_blk->next;
        p->free_cnt --;
        return ENET_RET_SUCCESS;
    } else {
        /* TODO: calloc from heap? */
        return ERR_UTIL_MP_NO_MORE_BLOCKS;
    }
}

ret_t mp_alloc_n(mp_id id, uint8_t **block, uint32_t n) {
    pool *p = NULL;

    if (id < 0 || id >= MP_MAX_POOL || !block) {
        return ERR_UTIL_MP_INVALID_PARAMETERS;
    }

    p = (pool*)&g_pool_info.pools[id];

    if (p->free_cnt >= n) {
        *block = (uint8_t*)p->free_blk;
        p->free_cnt -= n;
        while (n--) {
            p->free_blk = p->free_blk->next;
        }
    } else {
        /* TODO: calloc from heap? */
        return ERR_UTIL_MP_NO_MORE_BLOCKS;
    }

    return ENET_RET_SUCCESS;
}

ret_t mp_free(mp_id id, const uint8_t *block) {
    pool *p = NULL;

    if (0 == id || id >= MP_MAX_POOL || !block) {
        return ERR_UTIL_MP_INVALID_PARAMETERS;
    }

    p = (pool*)&g_pool_info.pools[id];

    ((mp_blk* )block)->next = p->free_blk;
    p->free_blk = (mp_blk*)block;
    p->free_cnt ++;

    return ENET_RET_SUCCESS;
}
